package com.insurance.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.insurance.pojo.JwtResult;
import io.jsonwebtoken.*;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.util.Date;

/**
 * Created with IntelliJ IDEA.
 * User: 朱雄
 * Date: 2020/10/21 18:39
 * Description:
 * Version: V1.0
 */
public class JwtUtil {

    //token保留时间
    public static final long TTMILLS=30*60*1000;
    /**
     *
    服务器的key，用于做加解密的key数据。 如果可以使用客户端生成的key，当前定义的常量可以不使用。
     */
    private static final String JWT_SECERT = "bj232_jwt_secert";
    // jackson中数据对象绑定
    private static final ObjectMapper MAPPER = new ObjectMapper();
    // Token过期
    public static final int JWT_ERRCODE_EXPIRE = 1005;
    /**
    // 验证不通过
     *
     */
    public static final int JWT_ERRCODE_FAIL = 1006;

    /**
     *
     * 签发JWT，创建token的方法。
     * @param id jwt的唯一身份标识，一般用uuid。主要用来作为一次性token,从而回避重放攻击。
     * @param iss jwt签发者 gxa_jwt_test
     * @param subject jwt所面向的用户，payload中记录的public claims，当前环境中就是用户的登录名。
     * @param ttmills 有效期,单位毫秒
     * @return token是一次性的，是为一个用户的有效登录周期准备的一个token。用户退出或超时，token失效。

     */
    public  static String createJwt(String id,String iss,String subject,long ttmills){
        //加密算法
        SignatureAlgorithm signatureAlgorithm=SignatureAlgorithm.HS256;
        //当前系统时间
        long nowMillis=System.currentTimeMillis();
        //当前时间的日期对象
        Date now=new Date(nowMillis);
        //获得key
        SecretKey secretKey=generalKey();
        //创建JWT的构建器，就是使用指定的信息和加密算法，生成token的工具
        JwtBuilder builder= Jwts.builder()
                //设置身份表示，UUID
                .setId(id)
                //签发者
                .setIssuer(iss)
                //登录的用户名
                .setSubject(subject)
                //token的生成时间
                .setIssuedAt(now)
                //设置算法和密钥
                .signWith(signatureAlgorithm,secretKey);
        //判断token有效时间是否大于0
        if (ttmills>0){
            long expMillis=nowMillis+ttmills;
            //token失效时间
            Date expDate=new Date(expMillis);
            //token失效
            builder.setExpiration(expDate);
        }
        //生成token并返回
        return builder.compact();
    }

    private static SecretKey generalKey() {
        // byte[] encodedKey = Base64.decode(JWT_SECERT);
        // 无论哪种方式最终得到一个byte[]类型的key就行
        try {
            byte[] encodeKey=JWT_SECERT.getBytes("UTF-8");
            SecretKey key=new SecretKeySpec(encodeKey,0,encodeKey.length,"AES");
            return key;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }

    }

    /**
     *验证Jwt
     *
     * @param jwtStr token
     * @return
     */
    public static JwtResult validateJwt(String jwtStr){
        JwtResult checkResult=new JwtResult();
        Claims claims=null;
        try {
            claims = parseJWT(jwtStr);
            checkResult.setSuccess(true);
            checkResult.setClaims(claims);
        } catch (ExpiredJwtException e) { // token超时
            checkResult.setErrCode(JWT_ERRCODE_EXPIRE);
            checkResult.setSuccess(false);
        } catch (SignatureException e) { // 校验失败
            checkResult.setErrCode(JWT_ERRCODE_FAIL);
            checkResult.setSuccess(false);
        } catch (Exception e) {
            checkResult.setErrCode(JWT_ERRCODE_FAIL);
            checkResult.setSuccess(false);
        }
        return checkResult;

    }

    /**
     * 解析JWT字符串
     * @param jwtStr 就是服务器为客户端生成的签名数据，就是token。
     * @return
     */
    private static Claims parseJWT(String jwtStr) {
        SecretKey secretKey=generalKey();
        return Jwts.parser().setSigningKey(secretKey)
                .parseClaimsJws(jwtStr).getBody();
    }

    /**
     *
     * 生成subject信息,一般传入对象是使用的用户实体类型，最好不包含任何敏感数据，比如只有用户名
     * @param subject - 要转换的对象。
     * @return java对象->JSON字符串出错时返回null

     */
    public static String generalSubject(Object subject){
        try {
            return MAPPER.writeValueAsString(subject);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return null;
        }
    }
}
